﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using MToolStudio.Render;
using MToolStudio.UI;
using Microsoft.Mtool.FSM;
using System.Xml;
using System.Xml.Serialization;

namespace MToolStudio
{
    public partial class FSMDesigner : UserControl
    {
        private List<IRenderable> renderItems = new List<IRenderable>();
        private FSM machine = new FSM();
        private UITool currentTool;
        private IRenderable focusedObject;
        private Point renderTranslation;
        private float renderScale = 1;

        public EventHandler<KeyEventArgs> OnCharInputToFocusedObject;
                
        public FSMDesigner()
        {
            InitializeComponent();
        }

        public FSM Machine
        {
            get { return machine; }            
        }

        public List<IRenderable> RenderItems
        {
            get { return renderItems; }
        }

        public Point RenderTranslation
        {
            get { return renderTranslation; }
            set { renderTranslation = value; }
        }

        public float RenderScale
        {
            get { return renderScale; }
            set { renderScale = value; }
        }
        
        public UITool CurrentTool
        {
            get { return currentTool; }
            set 
            {
                if (currentTool != value)
                {
                    currentTool = value;
                    if (currentTool != null)
                        OnCurrentUIToolChanged(this, new UIToolEventArgs(currentTool));
                }
            }
        }

        public event EventHandler<UIToolEventArgs> OnCurrentUIToolChanged;

        public event EventHandler<UIFocusChangedEventArgs> OnFocusObjectChanged;

        public IRenderable FocusedObject
        {
            get { return focusedObject; }
            set 
            {
                if (focusedObject != value)
                {
                    focusedObject = value;
                }
                //put it outside the above loop
                //in order to make targetting/inputxml boxes update when dragging new token onto currently focused existing-token actions
                if (OnFocusObjectChanged != null)
                    OnFocusObjectChanged(this, new UIFocusChangedEventArgs(value));
            }
        }

        public List<TourR> FocusedTours
        {
            set 
            {                
                List<IRenderable> curTours = new List<IRenderable>();
                foreach (IRenderable ir in renderItems)
                {
                    if (ir is TourR)
                        curTours.Add(ir);
                }
                foreach (IRenderable ir in curTours)
                    renderItems.Remove(ir);
                if (value != null)
                {
                    foreach (TourR tr in value)
                        renderItems.Add(tr);
                }
            }            
        }

        public Dictionary<string, IRenderable> GetUIMapping()
        {
            Dictionary<string, IRenderable> retv = new Dictionary<string, IRenderable>();
            foreach (IRenderable ir in renderItems)
            {
                if (ir is ActionR)
                {
                    retv.Add((ir as ActionR).InnerAction.Id, ir);
                }
                else if (ir is StateR)
                {
                    retv.Add((ir as StateR).InnerState.Id, ir);
                }
            }
            return retv;
        }

        public StateR AddState(Point pt, StateType stateType)
        {
            //FSM
            State st = new State(IdGenerator.Instance.NextStateId(), stateType);
            machine.States.Add(st);

            //GUI
            StateR sr = new StateR(st);
            sr.Position = new Point(pt.X, pt.Y);
            renderItems.Add(sr);
            Refresh();

            return sr;
        }

        public ActionR AddAction(Point pt, StateR from, StateR to)
        {
            //FSM
            Action act = new Action(IdGenerator.Instance.NextActionId(), from == to ? ActionType.Control : ActionType.Control, 
                ActionSource.Token, from.InnerState, to.InnerState);
            machine.Actions.Add(act);            

            //GUI
            ActionR ar = new ActionR(act, from, to);
            RenderItems.Add(ar);
            Refresh();

            return ar;
        }

        public void DeleteObject(IRenderable obj)
        {
            if (obj == null)
                return;
            if (obj is ActionR)
            {
                renderItems.Remove(obj);
                Machine.Actions.Remove((obj as ActionR).InnerAction);
                IdGenerator.Instance.RemoveActionId((obj as ActionR).InnerAction.Id);
                focusedObject = null;
                Refresh();
            }
            else if (obj is StateR)
            {
                StateR toRemove = obj as StateR;
                bool hasLink = false;
                foreach (Action ac in machine.Actions)
                {
                    if (ac.From == toRemove.InnerStateId || ac.To == toRemove.InnerStateId)
                    {
                        hasLink = true;
                        break;
                    }
                }
                if (hasLink)
                    return;//need to remove the actions linked to state first before deleting a state
                
                renderItems.Remove(toRemove);
                machine.States.Remove(toRemove.InnerState);
                IdGenerator.Instance.RemoveStateId(toRemove.InnerState.Id);
                if (toRemove == focusedObject)
                    focusedObject = null;
                Refresh();
            }
            else
            {
                //ignore it
            }
        }

        private void FSMDesigner_Paint(object sender, PaintEventArgs e)
        {
            //Logger.Instance.Verbose("FSMDesigner_Paint: {0} , {1}", renderTranslation.X, renderTranslation.Y);

            e.Graphics.ScaleTransform(RenderScale, RenderScale);
            e.Graphics.TranslateTransform(renderTranslation.X, renderTranslation.Y);
                        
            UITool.SetTransform(e.Graphics);

            foreach (IRenderable sr in renderItems)
            {
                sr.PrePaint(e.Graphics);
            }
            foreach (IRenderable sr in renderItems)
            {
                sr.Paint(e.Graphics);
            }
            if (focusedObject != null)
                focusedObject.PaintFocus(e.Graphics);
            if (currentTool != null)
                currentTool.Render(e.Graphics);
            foreach (IRenderable sr in renderItems)
            {
                sr.PostPaint(e.Graphics);
            }
           // e.Graphics.TranslateClip(renderTranslation.X, renderTranslation.Y);
        }

        private void FSMDesigner_MouseClick(object sender, MouseEventArgs e)
        {

        }

        private void FSMDesigner_MouseLeave(object sender, EventArgs e)
        {

        }

        private void FSMDesigner_MouseMove(object sender, MouseEventArgs e)
        {
            if (currentTool != null)
                currentTool.OnMouseMove(sender, e);
        }

        private void FSMDesigner_MouseUp(object sender, MouseEventArgs e)
        {
            if (currentTool != null)
                currentTool.OnMouseUp(sender, e);
        }

        private void FSMDesigner_MouseDown(object sender, MouseEventArgs e)
        {
            if (currentTool != null)
                currentTool.OnMouseDown(sender, e);
        }

        private void FSMDesigner_DragDrop(object sender, DragEventArgs e)
        {
           
        }

        private void FSMDesigner_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (currentTool != null)
                currentTool.OnKeyPress(sender, e);
        }

        private void FSMDesigner_KeyDown(object sender, KeyEventArgs e)
        {
            //renaming
            if (focusedObject != null && e.KeyCode == Keys.F2)
            {
                if (OnCharInputToFocusedObject != null)
                {
                    OnCharInputToFocusedObject(this, e);
                }
            }           
            if (currentTool != null)
                currentTool.OnKeyDown(sender, e);
        }

        public void LoadFromStudioData(MToolStudioData data)
        {
            Reset();

            machine = data.Machine;
            machine.Initialize();

            Dictionary<string, StateR> stateMap = new Dictionary<string, StateR>();
            foreach (StateR sr in data.States)
            {
                sr.PostSerializationInit(machine);
                renderItems.Add(sr);
                stateMap.Add(sr.InnerState.Id, sr);
            }

            foreach (ActionR ar in data.Actions)
            {
                ar.PostSerializationInit(machine, stateMap);
                renderItems.Add(ar);
            }

            Refresh();
        }

        public void Reset()
        {
            focusedObject = null;
            renderItems.Clear();
            currentTool = new DefaultActionTool(this);
            machine = new FSM();
            RenderScale = 1f;
            RenderTranslation = new Point(0, 0);
            Refresh();
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {            
            if (currentTool != null)
                currentTool.OnMouseScroll(this, e);
        }
    }
}
